package com.lcf.framework.utils.oss;

import cn.hutool.core.collection.CollectionUtil;
import com.lcf.base.exception.SysException;
import com.lcf.framework.config.oss.OssConfig;
import com.lcf.framework.enums.OssTypeEnum;
import com.lcf.framework.utils.oss.ali.AliOssUtils;
import com.lcf.framework.utils.oss.minio.MinioOssUtils;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.util.*;

/**
 * oss
 *
 * @author lcf
 */
@Component
public class OssUtils {
    @Resource
    private OssConfig ossConfig;
    private static IOssUtils ossUtils;
    @Resource
    private AliOssUtils aliOssUtils;
    @Resource
    private MinioOssUtils minioOssUtils;

    private static final Map<String, String> CONTENT_TYPE_MAP = new HashMap<>();
    static {
        // 图像类型
        CONTENT_TYPE_MAP.put(".bmp", "image/bmp");
        CONTENT_TYPE_MAP.put(".gif", "image/gif");
        CONTENT_TYPE_MAP.put(".jpeg", "image/jpeg");
        CONTENT_TYPE_MAP.put(".jpg", "image/jpeg");
        CONTENT_TYPE_MAP.put(".png", "image/png");
        CONTENT_TYPE_MAP.put(".svg", "image/svg+xml");
        CONTENT_TYPE_MAP.put(".webp", "image/webp");
        CONTENT_TYPE_MAP.put(".ico", "image/vnd.microsoft.icon");

        // 视频类型
        CONTENT_TYPE_MAP.put(".mp4", "video/mp4");
        CONTENT_TYPE_MAP.put(".mkv", "video/x-matroska");
        CONTENT_TYPE_MAP.put(".avi", "video/x-msvideo");
        CONTENT_TYPE_MAP.put(".mov", "video/quicktime");
        CONTENT_TYPE_MAP.put(".wmv", "video/x-ms-wmv");
        CONTENT_TYPE_MAP.put(".webm", "video/webm");

        // 音频类型
        CONTENT_TYPE_MAP.put(".mp3", "audio/mpeg");
        CONTENT_TYPE_MAP.put(".wav", "audio/wav");
        CONTENT_TYPE_MAP.put(".ogg", "audio/ogg");
        CONTENT_TYPE_MAP.put(".flac", "audio/flac");
        CONTENT_TYPE_MAP.put(".aac", "audio/aac");
        CONTENT_TYPE_MAP.put(".wma", "audio/x-ms-wma");

        // 文档类型
        CONTENT_TYPE_MAP.put(".html", "text/html");
        CONTENT_TYPE_MAP.put(".htm", "text/html");
        CONTENT_TYPE_MAP.put(".txt", "text/plain");
        CONTENT_TYPE_MAP.put(".xml", "text/xml");
        CONTENT_TYPE_MAP.put(".json", "application/json");
        CONTENT_TYPE_MAP.put(".csv", "text/csv");
        CONTENT_TYPE_MAP.put(".pdf", "application/pdf");

        // 压缩文件
        CONTENT_TYPE_MAP.put(".zip", "application/zip");
        CONTENT_TYPE_MAP.put(".rar", "application/vnd.rar");
        CONTENT_TYPE_MAP.put(".7z", "application/x-7z-compressed");
        CONTENT_TYPE_MAP.put(".gz", "application/gzip");
        CONTENT_TYPE_MAP.put(".tar", "application/x-tar");

        // Office文档
        CONTENT_TYPE_MAP.put(".doc", "application/msword");
        CONTENT_TYPE_MAP.put(".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        CONTENT_TYPE_MAP.put(".xls", "application/vnd.ms-excel");
        CONTENT_TYPE_MAP.put(".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        CONTENT_TYPE_MAP.put(".ppt", "application/vnd.ms-powerpoint");
        CONTENT_TYPE_MAP.put(".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
        CONTENT_TYPE_MAP.put(".vsd", "application/vnd.visio");

        // 字体文件
        CONTENT_TYPE_MAP.put(".ttf", "font/ttf");
        CONTENT_TYPE_MAP.put(".otf", "font/otf");
        CONTENT_TYPE_MAP.put(".woff", "font/woff");
        CONTENT_TYPE_MAP.put(".woff2", "font/woff2");

        // 其他常见类型
        CONTENT_TYPE_MAP.put(".js", "application/javascript");
        CONTENT_TYPE_MAP.put(".css", "text/css");
        CONTENT_TYPE_MAP.put(".map", "application/json"); // Source map files
        CONTENT_TYPE_MAP.put(".wasm", "application/wasm");
        CONTENT_TYPE_MAP.put(".apk", "application/vnd.android.package-archive");
    }

    public static String getContentType(String fileSuffix) {
        if (fileSuffix == null) {
            return "application/octet-stream";
        }

        String contentType = CONTENT_TYPE_MAP.get(fileSuffix.toLowerCase());
        return contentType != null ? contentType : "application/octet-stream";
    }

    /**
     * 初始化静态成员
     */
    @PostConstruct
    private void init() {
        OssConfig ossConfig = this.ossConfig;
        if (Objects.equals(ossConfig.getType(), OssTypeEnum.ALI.getName())) {
            ossUtils = this.aliOssUtils;
        }

        if (Objects.equals(ossConfig.getType(), OssTypeEnum.MINIO.getName())) {
            ossUtils = this.minioOssUtils;
        }
    }

    /**
     * 上传图片
     *
     * @param file       文件
     * @param path       相对路径
     * @param isCompress 是否压缩
     */
    public static String uploadImg(String path, MultipartFile file, boolean isCompress) {
        checkImg(file.getOriginalFilename());
        return ossUtils.uploadImg(path, file, isCompress);
    }

    /**
     * 上传图片
     *
     * @param path       相对路径
     * @param isCompress 是否压缩
     */
    public static String uploadImg(String path, InputStream inputStream, boolean isCompress) {
        return ossUtils.uploadImg(path, inputStream, isCompress);
    }

    /**
     * 上传图片
     *
     * @param url        文件url
     * @param path       相对路径
     * @param isCompress 是否压缩
     */
    public static String uploadImg(String path, String url, boolean isCompress) {
        checkImg(url);
        return ossUtils.uploadImg(path, url, isCompress);
    }

    /**
     * 更新图片
     *
     * @param inputStream 图片流
     * @param oldName     旧图片名
     */
    public static String updateImg(String bucket, InputStream inputStream, String oldName, boolean isCompress) {
        return ossUtils.updateImg(bucket, inputStream, oldName, isCompress);
    }

    /**
     * 更新图片
     *
     * @param file    图片
     * @param oldName 旧图片名
     */
    public static String updateImg(String bucket, MultipartFile file, String oldName, boolean isCompress) {
        return ossUtils.updateImg(bucket, file, oldName, isCompress);
    }

    /**
     * 更新图片
     *
     * @param fileUrl 新图片路径
     * @param oldName 旧图片名
     */
    public static String updateImg(String bucket, String fileUrl, String oldName, boolean isCompress) {
        return ossUtils.updateImg(bucket, fileUrl, oldName, isCompress);
    }

    /**
     * 上传文件
     *
     * @param file 文件
     * @param path 相对路径
     */
    public static String uploadFile(String path, MultipartFile file) {
        return ossUtils.uploadFile(path, file);
    }

    /**
     * 上传文件
     *
     * @param url  文件url
     * @param path 相对路径
     */
    public static String uploadFile(String path, String url) {
        return ossUtils.uploadFile(path, url);
    }

    /**
     * 更新文件
     *
     * @param inputStream 文件流
     * @param oldName     旧文件名
     */
    public static String updateFile(String bucket, InputStream inputStream, String oldName) {
        return ossUtils.updateFile(bucket, inputStream, oldName);
    }

    /**
     * 更新文件
     *
     * @param file    文件
     * @param oldName 旧文件名
     */
    public static String updateFile(String bucket, MultipartFile file, String oldName) {
        return ossUtils.updateFile(bucket, file, oldName);
    }

    /**
     * 更新文件
     *
     * @param fileUrl 新文件路径
     * @param oldName 旧文件名
     */
    public static String updateFile(String bucket, String fileUrl, String oldName) {
        return ossUtils.updateFile(bucket, fileUrl, oldName);
    }

    /**
     * 删除文件
     */
    public static void delFile(String fileUrl) {
        if (StringUtils.isBlank(fileUrl)) {
            return;
        }

        fileUrl = getRelativeUrl(fileUrl);
        if (StringUtils.isBlank(fileUrl) || !fileUrl.contains("/")) {
            return;
        }

        String[] path = fileUrl.split("/");
        ossUtils.delFile(path[0], path[1]);
    }

    /**
     * 获取文件绝对路径
     */
    public static String getAbsoluteUrl(String fileName) {
        return ossUtils.getAbsoluteUrl(fileName);
    }

    /**
     * 获取文件相对路径
     */
    public static String getRelativeUrl(String url) {
        return ossUtils.getRelativeUrl(url);
    }

    /**
     * 根据相对路径获取绝对路径map
     */
    public static Map<String, String> getAbsoluteUrlMap(List<String> filePathList) {
        Map<String, String> map = new HashMap<>();
        if (CollectionUtil.isEmpty(filePathList)) {
            return map;
        }

        filePathList.forEach(filePath -> map.put(filePath, getAbsoluteUrl(filePath)));
        return map;
    }

    /**
     * 根据绝对路径获取相对路径map
     */
    public static Map<String, String> getRelativeMap(List<String> filePathList) {
        Map<String, String> map = new HashMap<>();
        if (CollectionUtil.isEmpty(filePathList)) {
            return map;
        }

        filePathList.forEach(filePath -> map.put(filePath, getRelativeUrl(filePath)));
        return map;
    }

    /**
     * 获取base64图片
     */
    public static String getBase64Img(String fileName) {
        String imgUrl = getAbsoluteUrl(fileName);
        try (InputStream inputStream = new URL(imgUrl).openStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int ch;
            while ((ch = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, ch);
            }

            return Base64.getEncoder().encodeToString(outputStream.toByteArray());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 检查图片
     */
    public static void checkImg(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            throw new SysException("请上传图片");
        }
        String[] suffixList = {"bmp", "jpg", "jpeg", "gif", "png"};
        if (Arrays.stream(suffixList).noneMatch(fileName::endsWith)) {
            throw new SysException("请上传图片");
        }
    }

    /**
     * 获取文件大小
     * @param fileName 文件名称
     */
    public static String getFileSize(String fileName) {
        Long bytes = ossUtils.getFileSize(fileName);

        BigDecimal rate = new BigDecimal(1024);
        RoundingMode roundingMode = RoundingMode.HALF_UP;

        BigDecimal kb = bytes == null ? BigDecimal.ZERO : new BigDecimal(bytes)
                .divide(rate, 2, roundingMode);

        return BigDecimal.ZERO.compareTo(kb) == 0 ? "0" : kb.compareTo(rate) < 0 ? kb + "KB"
                : kb.divide(rate, 2, roundingMode) + "MB";
    }
}
